﻿#include"XQZipPack.hpp"
#include"QCoding.h"
#include "zlib/unzip.h"
#include "zlib/zip.h"
#include<QDir>
QByteArray XQZipPack::gzip(const QByteArray& data, int leve)
{
	z_stream c_stream;
	int err = 0;
	int windowBits = 15;
	int GZIP_ENCODING = 16;
	//MAX_WBITS
	if (data.isEmpty())
		return QByteArray();//空的

	QByteArray outData;//输出的数据数组
	outData.resize(data.size()+100);//重置大小
	c_stream.zalloc = (alloc_func)0;
	c_stream.zfree = (free_func)0;
	c_stream.opaque = (voidpf)0;
	if (deflateInit2(&c_stream, leve, Z_DEFLATED,
		windowBits | GZIP_ENCODING, 8, Z_DEFAULT_STRATEGY) != Z_OK) 
	{
		return QByteArray();
	}
	c_stream.next_in = (Bytef*)data.data();
	c_stream.avail_in = data.capacity();
	c_stream.next_out = (Bytef*)&outData.front();
	c_stream.avail_out = outData.size();
	while (c_stream.avail_in != 0 && c_stream.total_out < outData.size())
	{
		if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK) 
		{
			return QByteArray();
		}
	}
	if (c_stream.avail_in != 0) 
	{
		return QByteArray();
	}
	do
	{
		err = deflate(&c_stream, Z_FINISH);
	} while (err != Z_STREAM_END);
	if (deflateEnd(&c_stream) != Z_OK) 
	{
		return QByteArray();
	}
	outData.resize(c_stream.total_out);
	return std::move(outData);
}
void XQZipPack::package(const QString& packagePath, const QString& passWord, int level)
{
	if (m_dirs.count() == 0 && m_files.count() == 0)
		return;
	m_zipInfo.clear();
	m_zipInfo.type = ZipPackType::gzip;
	emit zipStart(m_zipInfo);
	zipFile newZipFile = zipOpen(packagePath.toLocal8Bit().data(), APPEND_STATUS_CREATE); //创建zip文件
	if (newZipFile == NULL)
	{
		emit progressInfo(QString("创建打包文件失败:") + packagePath);
		return;
	}
	//总任务量
	size_t fileCount = 0;//文件总数
	size_t fileSize = 0;
	emit progressInfo("正在计算文件个数.....");
	for (auto& path: m_dirs)
	{
		QFileInfoList list;
		fileCount+=fileListSize(path, &list);
		fileSize += StatisticalDirFileSize(list);
	}
	fileCount += m_files.count();

	//emit fileListFinish(m_fileList);//统计文件完成
	emit progressInfo(QString("统计文件完成:") + QString::number(fileCount)+"个文件");
	//m_zipInfo.clear();
	m_zipInfo.fileCount = fileCount;
	m_zipInfo.fileSumSize = fileSize;
	m_startTime = QDateTime::currentDateTime();
	//打包文件夹
	for (auto& path : m_dirs)
	{	
		packageByDir(&newZipFile, path, QDir(path).dirName(), passWord, level);
	}
	//打包文件
	for (auto& file : m_files)
	{
		QFileInfo fileInfo(file);
		packageByFile(&newZipFile, fileInfo.fileName(), fileInfo.filePath(), passWord, level);
	}
	emit zipProgress(m_zipInfo);
	//关闭zip文件
	emit progressInfo("压缩打包完成");
	zipCloseFileInZip(newZipFile);
	zipClose(newZipFile, NULL); //关闭zip文件
	emit zipFinish(m_zipInfo);
}
void XQZipPack::packageByDir(zipFile* zf, const QString& strPath, const QString& parentDir, const QString& passWord, int level)
{
	QString strRelativePath;//相对路径
	QDir dir(strPath);//需要打包的目录
	for (auto& mfi: dir.entryInfoList())
	{
		if (mfi.fileName() == "." || mfi.fileName() == "..")
			continue;//跳过当前目录和上级目录
		if (parentDir == "")//没有父目录
			strRelativePath = mfi.fileName();
		else
			strRelativePath = parentDir + "/" + mfi.fileName(); //生成在zip文件中的相对路径
		if (mfi.isFile())//当前是文件
		{
			packageByFile(zf, strRelativePath, mfi.filePath(), passWord, level);
		}
		else//是目录
		{
			packageByFile(zf, strRelativePath, NULL, passWord, level);
			emit progressInfo(QString::fromLocal8Bit("正在打包目录：") + strRelativePath);
			packageByDir(zf, mfi.filePath(), strRelativePath, passWord, level); //递归收集子目录文件
		}
	}
}
void XQZipPack::packageByFile(zipFile* zf, const QString& fileNameInPackage, const QString& filePath, const QString& passWord, int level)
{
	FILE* srcfp = NULL;
	//初始化写入zip的文件信息
	zip_fileinfo zi;
	zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
		zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
	zi.dosDate = 0;
	zi.internal_fa = 0;
	zi.external_fa = 0;

	//如果srcFile为空，加入空目录
	QString new_file_name= fileNameInPackage;
	if (filePath.isEmpty())
	{
		new_file_name.push_back('/');
	}
	
	//在zip文件中创建新文件
	zipOpenNewFileInZip3_64(*zf, new_file_name.toLocal8Bit().data(), &zi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, passWord.isEmpty() ? NULL : passWord.toLocal8Bit().data(), 0, 0);
	//zipOpenNewFileInZip3_64(*zf, new_file_name.toLocal8Bit().data(), NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, level, 0, MAX_WBITS, DEF_MEM_LEVEL,
		//Z_DEFAULT_STRATEGY, passWord.isEmpty() ? NULL : passWord.toLocal8Bit().data(), 0, 0);

	if (!filePath.isEmpty())
	{
		//打开源文件
		QFile file(filePath);
		if (!file.open(QIODeviceBase::OpenModeFlag::ReadOnly))
		{
			zipCloseFileInZip(*zf); //关闭zip文件
			return;
		}
		m_zipInfo.setFile(QFileInfo(filePath));
		m_zipInfo.addIndexes();
		//m_zipInfo.addSize(file.size());
		//读入源文件并写入zip文件
		char* buf=new char[BUFFER_SIZE]; //缓冲区
		int numBytes = 0;
		while (!file.atEnd())
		{
			numBytes = file.read(buf, BUFFER_SIZE);
			zipWriteInFileInZip(*zf, buf, numBytes);
			m_zipInfo.addSize(numBytes);
			sendProgress();
		}
		//关闭
		delete[]buf;
	}
	//关闭zip文件
	emit progressInfo(QString::fromLocal8Bit("正在打包文件：") + new_file_name);
}
